home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 27 / CU Amiga Magazine's Super CD-ROM 27 (1998)(EMAP Images)(GB)[!][issue 1998-10].iso / CUCD / PowerPC / vbcc / machines / amigawos / libsrc / stdio / vfprintf.c < prev    next >
C/C++ Source or Header  |  1998-08-02  |  11KB  |  381 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdarg.h>
  4. #include <limits.h>
  5. #include <ctype.h>
  6. #include <math.h>
  7. #include <float.h>
  8.  
  9. /* a little macro to make life easier */
  10.  
  11. #define OUT(c)  do                           \
  12.                 { if(putc((c),stream)==EOF) \
  13.                     return outcount;         \
  14.                   outcount++;                \
  15.                 }while(0)
  16.  
  17. #define BITSPERBYTE CHAR_BIT
  18.  
  19. #define MINFLOATSIZE (DBL_DIG+1) /* Why not 1 more - it's 97% reliable */
  20. #define MININTSIZE (sizeof(unsigned long)*BITSPERBYTE/3+1)
  21. #define MINPOINTSIZE (sizeof(void *)*BITSPERBYTE/4+1)
  22. #define REQUIREDBUFFER (MININTSIZE>MINPOINTSIZE? \
  23.                         (MININTSIZE>MINFLOATSIZE?MININTSIZE:MINFLOATSIZE): \
  24.                         (MINPOINTSIZE>MINFLOATSIZE?MINPOINTSIZE:MINFLOATSIZE))
  25.  
  26. #define ALTERNATEFLAG 1  /* '#' is set */
  27. #define ZEROPADFLAG   2  /* '0' is set */
  28. #define LALIGNFLAG    4  /* '-' is set */
  29. #define BLANKFLAG     8  /* ' ' is set */
  30. #define SIGNFLAG      16 /* '+' is set */
  31.  
  32.  
  33. int vfprintf(FILE *stream,const char *format,va_list args)
  34. {
  35.   size_t outcount=0;
  36.  
  37.   while(*format)
  38.   {
  39.     if(*format=='%')
  40.     {
  41.       static char flagc[]=
  42.       { '#','0','-',' ','+' };
  43.       static char lowertabel[]=
  44.       { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
  45.       static char uppertabel[]=
  46.       { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
  47.       size_t width=0,preci=ULONG_MAX,flags=0; /* Specifications */
  48.       char type,subtype='i';
  49.       char buffer1[2];             /* Signs and that like */
  50.       char buffer[REQUIREDBUFFER]; /* The body */
  51.       char *buffer2=buffer;        /* So we can set this to any other strings */
  52.       size_t size1=0,size2=0;      /* How many chars in buffer? */
  53.       const char *ptr=format+1;    /* pointer to format string */
  54.       size_t i,pad;                /* Some temporary variables */
  55.  
  56.       do /* read flags */
  57.         for(i=0;i<sizeof(flagc);i++)
  58.           if(flagc[i]==*ptr)
  59.           { flags|=1<<i;
  60.             ptr++;
  61.             break; }
  62.       while(i<sizeof(flagc));
  63.  
  64.       if(*ptr=='*') /* read width from arguments */
  65.       { signed int a;
  66.         ptr++;
  67.         a=va_arg(args,signed int);
  68.         if(a<0)
  69.         { flags|=LALIGNFLAG;
  70.           width=-a; }
  71.         else
  72.           width=a;
  73.       }else
  74.         while(isdigit(*ptr))
  75.           width=width*10+(*ptr++-'0');
  76.  
  77.       if(*ptr=='.')
  78.       { ptr++;
  79.         if(*ptr=='*') /* read precision from arguments */
  80.         { signed int a;
  81.           ptr++;
  82.           a=va_arg(args,signed int);
  83.           if(a>=0)
  84.             preci=a;
  85.         }else
  86.         { preci=0;
  87.           while(isdigit(*ptr))
  88.             preci=preci*10+(*ptr++-'0');
  89.         }
  90.       }
  91.  
  92.       if(*ptr=='h'||*ptr=='l'||*ptr=='L')
  93.         subtype=*ptr++;
  94.  
  95.       type=*ptr++;
  96.  
  97.       switch(type)
  98.       { case 'd':
  99.         case 'i':
  100.         case 'o':
  101.         case 'p':
  102.         case 'u':
  103.         case 'x':
  104.         case 'X':
  105.         { unsigned long v;
  106.           char *tabel;
  107.           int base;
  108.  
  109.           if(type=='p')
  110.           { subtype='l'; /* This is written as %#lx */
  111.             type='x';
  112.             flags|=ALTERNATEFLAG; }
  113.  
  114.           if(type=='d'||type=='i') /* These are signed */
  115.           { signed long v2;
  116.             if(subtype=='l')
  117.               v2=va_arg(args,signed long);
  118.             else
  119.               v2=va_arg(args,signed int);
  120.             if(v2<0)
  121.             { buffer1[size1++]='-';
  122.               v=-v2;
  123.             }else
  124.             { if(flags&SIGNFLAG)
  125.                 buffer1[size1++]='+';
  126.               else if(flags&BLANKFLAG)
  127.                 buffer1[size1++]=' ';
  128.               v=v2; }
  129.           }else                    /* These are unsigned */
  130.           { if(subtype=='l')
  131.               v=va_arg(args,unsigned long);
  132.             else
  133.               v=va_arg(args,unsigned int);
  134.             if(flags&ALTERNATEFLAG)
  135.             { if(type=='o'&&(preci||v))
  136.                 buffer1[size1++]='0';
  137.               if((type=='x'||type=='X')&&v)
  138.               { buffer1[size1++]='0';
  139.                 buffer1[size1++]=type; }
  140.             }
  141.           }
  142.  
  143.           buffer2=&buffer[sizeof(buffer)]; /* Calculate body string */
  144.           base=type=='x'||type=='X'?16:(type=='o'?8:10);
  145.           tabel=type!='X'?lowertabel:uppertabel;
  146.           do
  147.           { *--buffer2=tabel[v%base];
  148.             v=v/base;
  149.             size2++;
  150.           }while(v);
  151.           if(preci==ULONG_MAX) /* default */
  152.             preci=0;
  153.           else
  154.             flags&=~ZEROPADFLAG;
  155.           break;
  156.         }
  157.         case 'c':
  158.           if(subtype=='l')
  159.             *buffer2=va_arg(args,long);
  160.           else
  161.             *buffer2=va_arg(args,int);
  162.           size2=1;
  163.           preci=0;
  164.           break;
  165.         case 's':
  166.           buffer2=va_arg(args,char *);
  167.           { char *p=buffer2;
  168.             size2=0;
  169.             while((width<=0||size2<width)&&*p){size2++;p++;}
  170.           }
  171.           size2=size2<=preci?size2:preci;
  172.           preci=0;
  173.           break;
  174. #ifdef MATH
  175.         case 'f':
  176.         case 'e':
  177.         case 'E':
  178.         case 'g':
  179.         case 'G':
  180.         { double v;
  181.           char killzeros=0,sign=0; /* some flags */
  182.           int ex1,ex2; /* Some temporary variables */
  183.           size_t size,dnum,dreq;
  184.           char *udstr=NULL;
  185.  
  186.           v=va_arg(args,double);
  187.  
  188.           if(isinf(v))
  189.           { if(v>0)
  190.               udstr="+inf";
  191.             else
  192.               udstr="-inf";
  193.           }else if(isnan(v))
  194.             udstr="NaN";
  195.  
  196.           if(udstr!=NULL)
  197.           { size2=strlen(udstr);
  198.             preci=0;
  199.             buffer2=udstr;
  200.             break; }
  201.  
  202.           if(preci==ULONG_MAX) /* old default */
  203.             preci=6; /* new default */
  204.  
  205.           if(v<0.0)
  206.           { sign='-';
  207.             v=-v;
  208.           }else
  209.           { if(flags&SIGNFLAG)
  210.               sign='+';
  211.             else if(flags&BLANKFLAG)
  212.               sign=' ';
  213.           }
  214.  
  215.           ex1=0;
  216.           if(v!=0.0)
  217.           { ex1=log10(v);
  218.             if(v<1.0)
  219.               v=v*pow(10,- --ex1); /* Caution: (int)log10(.5)!=-1 */
  220.             else
  221.               v=v/pow(10,ex1);
  222.             if(v<1.0) /* adjust if we are too low (log10(.1)=-.999999999) */
  223.             { v*=10.0; /* luckily this cannot happen with FLT_MAX and FLT_MIN */
  224.               ex1--; } /* The case too high (log(10.)=.999999999) is done later */
  225.           }
  226.  
  227.           ex2=preci;
  228.           if(type=='f')
  229.             ex2+=ex1;
  230.           if(tolower(type)=='g')
  231.             ex2--;
  232.           v+=.5/pow(10,ex2<MINFLOATSIZE?ex2:MINFLOATSIZE); /* Round up */
  233.  
  234.           if(v>=10.0) /* Adjusts log10(10.)=.999999999 too */
  235.           { v/=10.0;
  236.             ex1++; }
  237.  
  238.           if(tolower(type)=='g') /* This changes to one of the other types */
  239.           { if(ex1<(signed long)preci&&ex1>=-4)
  240.             { type='f';
  241.               preci-=ex1;
  242.             }else
  243.               type=type=='g'?'e':'E';
  244.             preci--;
  245.             if(!(flags&ALTERNATEFLAG))
  246.               killzeros=1; /* set flag to kill trailing zeros */
  247.           }
  248.  
  249.           dreq=preci+1; /* Calculate number of decimal places required */
  250.           if(type=='f')
  251.             dreq+=ex1;   /* even more before the decimal point */
  252.  
  253.           dnum=0;
  254.           while(dnum<dreq&&dnum<MINFLOATSIZE) /* Calculate all decimal places needed */
  255.           { buffer[dnum++]=(char)v+'0';
  256.             v=(v-(double)(char)v)*10.0; }
  257.  
  258.           if(killzeros) /* Kill trailing zeros if possible */
  259.             while(preci&&(dreq-->dnum||buffer[dreq]=='0'))
  260.               preci--;
  261.  
  262.           if(type=='f')/* Calculate actual size of string (without sign) */
  263.           { size=preci+1; /* numbers after decimal point + 1 before */
  264.             if(ex1>0)
  265.               size+=ex1; /* numbers >= 10 */
  266.             if(preci||flags&ALTERNATEFLAG)
  267.               size++; /* 1 for decimal point */
  268.           }else
  269.           { size=preci+5; /* 1 for the number before the decimal point, and 4 for the exponent */
  270.             if(preci||flags&ALTERNATEFLAG)
  271.               size++;
  272.             if(ex1>99||ex1<-99)
  273.               size++; /* exponent needs an extra decimal place */
  274.           }
  275.  
  276.           pad=size+(sign!=0);
  277.           pad=pad>=width?0:width-pad;
  278.  
  279.           if(sign&&flags&ZEROPADFLAG)
  280.             OUT(sign);
  281.  
  282.           if(!(flags&LALIGNFLAG))
  283.             for(i=0;i<pad;i++)
  284.               OUT(flags&ZEROPADFLAG?'0':' ');
  285.  
  286.           if(sign&&!(flags&ZEROPADFLAG))
  287.             OUT(sign);
  288.  
  289.           dreq=0;
  290.           if(type=='f')
  291.           { if(ex1<0)
  292.               OUT('0');
  293.             else
  294.               while(ex1>=0)
  295.               { OUT(dreq<dnum?buffer[dreq++]:'0');
  296.                 ex1--; }
  297.             if(preci||flags&ALTERNATEFLAG)
  298.             { OUT('.');
  299.               while(preci--)
  300.                 if(++ex1<0)
  301.                   OUT('0');
  302.                 else
  303.                   OUT(dreq<dnum?buffer[dreq++]:'0');
  304.             }
  305.           }else
  306.           { OUT(buffer[dreq++]);
  307.             if(preci||flags&ALTERNATEFLAG)
  308.             { OUT('.');
  309.               while(preci--)
  310.                 OUT(dreq<dnum?buffer[dreq++]:'0');
  311.             }
  312.             OUT(type);
  313.             if(ex1<0)
  314.             { OUT('-');
  315.               ex1=-ex1; }
  316.             else
  317.               OUT('+');
  318.             if(ex1>99)
  319.               OUT(ex1/100+'0');
  320.             OUT(ex1/10%10+'0');
  321.             OUT(ex1%10+'0');
  322.           }
  323.  
  324.           if(flags&LALIGNFLAG)
  325.             for(i=0;i<pad;i++)
  326.               OUT(' ');
  327.  
  328.           width=preci=0; /* Everything already done */
  329.           break;
  330.         }
  331. #endif
  332.         case '%':
  333.           buffer2="%";
  334.           size2=1;
  335.           preci=0;
  336.           break;
  337.         case 'n':
  338.           *va_arg(args,int *)=outcount;
  339.           width=preci=0;
  340.           break;
  341.         default:
  342.           if(!type)
  343.             ptr--; /* We've gone too far - step one back */
  344.           buffer2=(char *)format;
  345.           size2=ptr-format;
  346.           width=preci=0;
  347.           break;
  348.       }
  349.       pad=size1+(size2>=preci?size2:preci); /* Calculate the number of characters */
  350.       pad=pad>=width?0:width-pad; /* and the number of resulting pad bytes */
  351.  
  352.       if(flags&ZEROPADFLAG) /* print sign and that like */
  353.         for(i=0;i<size1;i++)
  354.           OUT(buffer1[i]);
  355.  
  356.       if(!(flags&LALIGNFLAG)) /* Pad left */
  357.         for(i=0;i<pad;i++)
  358.           OUT(flags&ZEROPADFLAG?'0':' ');
  359.  
  360.       if(!(flags&ZEROPADFLAG)) /* print sign if not zero padded */
  361.         for(i=0;i<size1;i++)
  362.           OUT(buffer1[i]);
  363.  
  364.       for(i=size2;i<preci;i++) /* extend to precision */
  365.         OUT('0');
  366.  
  367.       for(i=0;i<size2;i++) /* print body */
  368.         OUT(buffer2[i]);
  369.  
  370.       if(flags&LALIGNFLAG) /* Pad right */
  371.         for(i=0;i<pad;i++)
  372.           OUT(' ');
  373.  
  374.       format=ptr;
  375.     }
  376.     else
  377.       OUT(*format++);
  378.   }
  379.   return outcount;
  380. }
  381.